<script setup lang="ts">
import {computed, h, onMounted, ref, toRaw, watch} from 'vue'
import {BoxOpen} from '@vicons/fa';
import {ColumnHeightOutlined, QuestionCircleFilled} from '@vicons/antd';
import {ArrowSync20Filled} from '@vicons/fluent';
import {customProps} from './props.ts';
import CustomIcon from '@/components/CustomIcon'
import {useWindowSize} from '@vueuse/core'
import {appThemeColors} from '@/config'
import type {PaginationInfo} from 'naive-ui'
import {getRequest, postRequest} from "@/api";


interface SortChangeParams {
  columnKey: string | number | null;
  order: 'ascend' | 'descend' | false;
}

interface SorterParams {
  sort_by: null | string | number,
  descending: null | string | boolean,
}

type TableRow = Record<string, unknown>

interface ListResp {
  total: number
  list: TableRow[]
}

const {height} = useWindowSize()

const props = defineProps(customProps);

const dataTableRef = ref<HTMLElement | null>(null);
const tableDataList = ref<Recordable<unknown>[] | ListResponse>([]);
const tableLoading = ref<boolean>(false);

//数据表大小
const tableSize = ref<string>(props.size || "small");
const tableSizeOptions = [
  {
    label: '默认',
    key: 'small',
  }, {
    label: '较大',
    key: 'medium',
  }, {
    label: '特大',
    key: 'large',
  }
]
const sorterParams = ref<SorterParams>({
  sort_by: null,
  descending: null,
})

const currentPage = ref(1);
const currentPageSize = ref(props.pagination ? props.pagination?.pageSize : 15);

//查询条件
const searchDataParams = ref({...props.params});

// 监听props.params的变化
watch(() => props.params, (newParams) => {
  searchDataParams.value = {...newParams};
}, {deep: true});

//数据表高度
const tableHeight = computed(() => {
  return height.value - 330 - props.subHeight;
})
//分页处理
const pageSizesOptions = computed(() => {
  if (props.pagination && props.pagination.pageSizes) {
    return props.pagination.pageSizes.map((item: number) => {
      return {
        label: `每页 ${item} 条`,
        value: item
      }
    });
  } else {
    return [
      {label: "每页 15 条", value: 15},
      {label: "每页 30 条", value: 30},
      {label: "每页 50 条", value: 50},
      {label: "每页 100 条", value: 100}
    ]
  }
})

const paginationOptions = ref({
  defaultPage: currentPage.value,
  defaultPageSize: currentPageSize.value,
  showSizePicker: true,
  pageSizes: pageSizesOptions.value,
  showQuickJumper: true,
  itemCount: 0,
  pageSlot: 7,
  selectProps: {size: 'small'},
  pageCount: 1,
  prefix: (info: PaginationInfo) => {
    return `共 ${info.itemCount} 条数据`;
  },
});

//处理数据表
const TableBindValues = computed(() => {
  // 处理表头，先拷贝一份
  const dataColumns = Array.isArray(props.columns)
      ? props.columns.map((column) => {
        const column_item = toRaw(column);
        column_item['resizable'] = column_item['resizable'] == true || column_item['resizable'] == undefined ? true : column_item['resizable'];
        //处理排序
        if (column_item['sorter']) {
          column_item['sorter'] = 'default';
          column_item['renderSorterIcon'] = function ({order}: { order: 'ascend' | 'descend' | false | 'default' }) {
            const sort_icon: Recordable = {
              'ascend': 'carbon-CaretSortUp',
              'descend': 'carbon-CaretSortDown',
              'default': 'carbon-CaretSort'
            }
            if (order === false) {
              order = 'default';
            }
            return h(CustomIcon, {
              component: sort_icon[order],
              size: 18
            })
          }
        }
        //带提示的省略
        if (column_item['ellipsis'] || column_item['ellipsis'] == undefined) {
          const ellipsis_width = column_item['ellipsis_width'] ? column_item['ellipsis_width'] : 300;
          column_item['ellipsis'] = {
            tooltip: {
              contentStyle: {
                // width: `${ellipsis_width}px`
                maxWidth: `${ellipsis_width}px`,
              }
            },
          }
        }
        return column_item
      }) : [];

  // 复选框/单选框
  if (props.defaultSelection) {
    dataColumns.unshift({
      type: 'selection',
      width: 40,
      align: "center",
      fixed: 'left'
    });
  }

  // 序号
  if (props.defaultSerialNumber) {
    dataColumns.unshift({
      key: 'no',
      title: props.serialNumberTitle,
      width: 80,
      align: "center",
      fixed: 'left',
      render: (_: never, rowIndex: number) => {
        const number_index = currentPage.value === 1 ? rowIndex + 1 : currentPageSize.value * (currentPage.value - 1) + (rowIndex + 1);
        return h("span", null, number_index);
      }
    });
  }
  //操作列
  if (props.actionColumn) {
    if (props.actionColumn.isShow == undefined || props.actionColumn.isShow) {
      const actColumns = {
        key: 'operation',
        title: props.actionColumn.title || '操作',
        fixed: 'right',
        width: getColumnWidth(props.actionColumn.width) ?? 130,
        render: props.actionColumn.render,
        className: 'action-column',
      }
      dataColumns.push(actColumns)
    }
  }

  //处理分页
  const pagination = typeof props.pagination == 'boolean' && !props.pagination ? false : paginationOptions.value;
  return {
    ...props,
    rowKey: (row: Recordable) => row[props.rowKeyColumn] || row['id'],
    columns: dataColumns,
    singleLine: props.singleLine,
    pagination,
    remote: true,
  }
})
const getColumnWidth = (width: number | (() => number)): number => {
  return typeof width === 'function' ? width() : width;
};

//获取数据
const getListData = async () => {

  if (tableLoading.value) return; // 防止重复请求

  tableLoading.value = true;
  try {
    if (!props.url && props.data.length > 0) {
      tableDataList.value = props.data;
    } else {
      //从接口中获取数据
      const search_data_params = {
        ...searchDataParams.value,
        ...sorterParams.value,
      }
      if (typeof props.pagination != 'boolean') {
        search_data_params['page'] = currentPage.value;
        search_data_params['pageSize'] = currentPageSize.value;
      }
      const result = (props.methods === 'GET'
          ? await getRequest(props.url, search_data_params)
          : await postRequest(props.url, search_data_params)) as { data: ListResp }
      const raw: Recordable<unknown>[] | ListResponse = result.data;
      if (typeof raw.total === 'number') {
        paginationOptions.value.itemCount = raw.total;
      } else {
        // 如果 total 不是数字，设置默认值或处理错误
        paginationOptions.value.itemCount = 0;
      }
      if (!Array.isArray(raw) && typeof props.pagination != 'boolean') {
        tableDataList.value = raw.list;
      } else {
        tableDataList.value = raw;
      }
    }
  } catch (e) {
    console.error('获取数据失败:', e);
  } finally {
    tableLoading.value = false;
  }
}
//刷新数据
const reload = () => {
  getListData()
}
const searchData = () => {
  currentPage.value = 1;
  getListData()
}
//切换数据表大小
const changeTableSize = (size: string) => {
  tableSize.value = size;
}
//排序
const handleSorterChange = ({columnKey, order}: SortChangeParams) => {
  sorterParams.value.sort_by = order ? columnKey : null;
  sorterParams.value.descending = order ? order : null;
  getListData()
}
//跳转页面
const updatePage = (page: number) => {
  currentPage.value = page;
  getListData()
}
//切换每页显示条数
const updatePageSize = (size: number) => {
  currentPage.value = 1;
  currentPageSize.value = size;
  getListData()
}
onMounted(() => {
  getListData()
})

defineExpose({searchData, reload});
</script>

<template>
  <div class="data-table-list">
    <div class="data-table-list-header">
      <div class="data-table-list-header__left">
        <slot name="toolBarLeft"></slot>
        <n-space :style="{paddingLeft:$slots.toolBarLeft?'12px':0}">
          <span v-if="props.titles">{{ props.titles }}</span>
          <n-tooltip trigger="hover" v-if="props.titleTooltip" size="small">
            <template #trigger>
              <n-icon size="18" :color="appThemeColors.primaryColor">
                <QuestionCircleFilled/>
              </n-icon>
            </template>
            {{ props.titleTooltip }}
          </n-tooltip>
        </n-space>
      </div>
      <div class="data-table-list-header__right">
        <slot name="toolBarRight"></slot>
        <n-space style="gap:8px 8px;">
          <n-button size="small" strong secondary @click="reload">
            <n-icon :size="18">
              <ArrowSync20Filled/>
            </n-icon>
          </n-button>
          <n-dropdown trigger="hover"
                      v-model:value="tableSize"
                      :options="tableSizeOptions"
                      @select="changeTableSize"
                      show-arrow
                      size="small">
            <n-button size="small" strong secondary>
              <n-icon :size="18">
                <ColumnHeightOutlined/>
              </n-icon>
            </n-button>
          </n-dropdown>
        </n-space>
      </div>
    </div>
    <div class="data-table-list-content">
      <n-data-table
          ref="dataTableRef"
          v-bind="TableBindValues"
          :loading="tableLoading"
          :size="tableSize"
          :data="tableDataList"
          :max-height="tableHeight"
          :scrollbar-props="{size:0,trigger:'hover'}"
          @update:sorter="handleSorterChange"
          @update:page="updatePage"
          @update:page-size="updatePageSize">
        <template #empty>
          <n-empty :description="props.emptyText">
            <template #icon>
              <n-icon size="42">
                <BoxOpen/>
              </n-icon>
            </template>
          </n-empty>
        </template>
        <template #loading>
          <n-spin :show="props.loading">
            <template #description>
              <span :style="{color:appThemeColors.primaryColor}">{{ props.loadingText }}</span>
            </template>
          </n-spin>
        </template>
      </n-data-table>
    </div>
  </div>
</template>

<style scoped lang="scss">
.data-table-list {
  &-header {
    display: flex;
    justify-content: space-between;
    padding: 6px 0;

    &__left, &__right {
      display: flex;
      align-items: center;
    }

    &__right {
      &__act {
        padding-right: 8px;
        padding-left: 8px;
        height: 24px;
        background-color: #e8e8e8;
        cursor: pointer;
        border-radius: 3px;
        display: flex;
        align-items: center;
        justify-content: center;
      }
    }
  }

  :deep(.action-column) {
    padding: 4px 8px !important;
  }
}
</style>
